/** Project 3
 *
 * @Group Members
 * 	Brentton Garber - mrbrentgarber@gmail.com
 * 	Georgii Tkachuk - georgii.t@gmail.com
 * 	Lucas Bridge - ltbridge@asu.edu
 * 	Austin Burnham - burnhamaustin@gmail.com
 * 	Kevin Greenberg - kgreenbe@asu.edu
 * @Revision
 *
 * @Since
 *
 */

#include <iostream>
#include "Scanner.h"

/**
 * Constructor. it initializes the variables, and sets the filename that is passed. If the file
 * cannot be opened, it prints that out. 
 *
 * @param  &filename    This is the name of the file to analyze and output.
 * @return none
 */
Scanner::Scanner(const string &filename)
{
	/**
     * This keeps track of how far along we are in the line of code.
     */
	sourceLineIndex = 0;
    /**
     * the current line we are working on. Used to print the entore line before tokenizing it.
     */
	sourceLine = "";

    /**
     * create a new instance of the printer class
     */
	printer = new Print(filename);

    /**
     * This open the file specified and stores it in sourceFile
     */
	sourceFile = fopen(filename.c_str(), "r");

	// check if the file is open. if it is not, tell us that we failed.
	if(sourceFile == NULL)
		cout << "COULD NOT FIND FILE!" <<endl;

	char ch;
	int i;
    for(i = 0; i < 128; i++)
	{       
                charTable[i] = SPECIAL;
	}
	for(ch = '0'; ch <= '9'; ch++)
	{
		charTable[ch] = DIGIT;
	}
	for(ch = 'a'; ch <= 'z'; ch++)
	{
		charTable[ch] = LETTER;
	}
	for(ch = 'A'; ch <= 'Z'; ch++)
	{
		charTable[ch] = LETTER;
	}
       
}

/**
 * The destructor. It is not called in this program.                        
 *
 * @param  none          
 * @return none
 */
Scanner::~Scanner()
{
	// when the class is being destroyed, close the current file if it is open.
	if(sourceFile != NULL)
		fclose(sourceFile);
}

/**
 * This looks at the next character and then calls GetToken if it is the right kind.                           
 *
 * @param  &tokenizer   Passes the tokenizer so that we can use GetToken and other public methods
 * @return none
 */
void Scanner::ScanFile(Tokenizer &tokenizer)
{
    while(true)
	{
		char ch = GetCharacter();
        if(ch != '\0' && ch != '\n' && ch != '\r')
        {
            BackUpCharacter();
            tokenizer.GetToken(charTable);
            printer->PrintToken(tokenizer);
        }
        else 
        {
            if(!GetSourceLine()) break;
            printer->PrintLine(sourceLine);
            sourceLineIndex = 0;
        }
    }
}

/**
 * This simply returns the type of character it has found.                            
 *
 * @param  &tokChar     The character that we are referencing
 * @return the corresponding value in the charTable table
 */
CharCode Scanner::GetCharType(char &tokChar)
{
    return charTable[tokChar];
}

/**
 * Scans the file and stores the next line.                           
 *
 * It scans the file. It is is successfull, it stores the string under sourceLine, and returns
 * true.
 *
 * @param  none         
 * @return boolean of whether or not the line was corectly stored
 */
bool Scanner::GetSourceLine()
{
    /**
     * We store the incoming ling in this variable, check if it is right, and then store
     * the string in the more permanent variable sourceLine.
     */
	char source_buffer[MAX_SOURCE_LINE_LENGTH];
	
	// try and read from the file. Return false if we failed.
	if(fgets(source_buffer, MAX_SOURCE_LINE_LENGTH,sourceFile) == NULL) return false;

	// store the line in the private class variable 'sourceLine'
	sourceLine = source_buffer;

	// return true, since we found a new line
	return true;
}

/**
 * This skips of a Pascal-style comment                           
 *
 * Pascal comments are surrounded by brackets {}. This method is called when an open bracket is
 * found, and then points to the caracter after the closed bracket.
 *
 * @param  none           
 * @return none
 */
void Scanner::SkipComment()
{   
    while (sourceLine[sourceLineIndex] != '}')
    {
        if(sourceLineIndex < (int)sourceLine.length() - 1)
            sourceLineIndex++;
        else if(GetSourceLine() == true)
            sourceLineIndex = 0;
    }
}

/**
 * Points to the character before the one it is pointing to now, by changing the value of
 * sourceLineIndex
 *
 * @param  none            
 * @return none
 */
void Scanner::BackUpCharacter()
{       
        
	// if the source line is above 0, decrease the current index. aka backup from the current character.
	if(sourceLineIndex > 0) 
		sourceLineIndex--;
}

/**
 * Returns the next character in the input file. If there is no input file, it returns NULL.
 * It also changes the pointer to after the character it returns.
 *
 * @param  none           
 * @return The next character in the file.
 */
char Scanner::GetCharacter()
{
    	// if the source file is not open, return null
	if(sourceFile == NULL) return NULL;
        // if the source line index is less then the length of the sourceline-1, then increase the sourceLine index

        if(sourceLineIndex == sourceLine.length() || sourceLine.length() == 0)
        {
            sourceLineIndex = 0;
            return '\0';
        }

        sourceLineIndex++;
        // if the code gets this far, return the source line character.
	return sourceLine[sourceLineIndex-1];
}